package create

import (
	"bytes"
	"gitee.com/shuokeyun/kratos/cmd/sk-kratos/internal/base"
	"io/ioutil"
	"log"
	"os"
	"path"
	"regexp"
	"strings"
	"text/template"

	"github.com/AlecAivazis/survey/v2"

	"github.com/spf13/cobra"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

var CmdMessage = &cobra.Command{
	Use:   "proto-message",
	Short: "Create proto message mappings for database tables",
	Long:  "Create proto message mappings for database tables. Example: sk-kratos table proto-message *",
	Run:   messageRun,
}

func init() {
	CmdMessage.Flags().StringVarP(&configFile, "config", "c", "", "config.yaml path")
	CmdMessage.Flags().StringVarP(&dbSource, "source", "s", "", "mysql source")
	CmdMessage.Flags().StringVarP(&outDir, "target-dir", "t", "", "out dir")
	CmdMessage.Flags().StringVarP(&deleteTablePrefix, "delete-table-prefix", "d", "", "delete table name prefix")
	CmdMessage.Flags().StringVarP(&tablePrefix, "prefix", "p", "", "struct name prefix")
	CmdMessage.Flags().StringVarP(&tableSuffix, "suffix", "u", "", "struct name suffix")
}

func messageRun(cmd *cobra.Command, args []string) {
	if len(args) == 0 {
		log.Fatalf("Please specify the table")
	}
	tableName := args[0]
	source := parseMysqlSource()
	reg := regexp.MustCompile(`^.+:.+@tcp\(.+\)/(.+)[:^\?:]`)
	match := reg.FindStringSubmatch(source)
	if len(match) < 2 {
		log.Fatalf("Failed to resolve the database name：%s\n", source)
	}
	dbName := match[1]
	DB, err := gorm.Open(mysql.Open(source))
	if err != nil {
		log.Fatalf("Database connection failure：%s\n", err.Error())
	}
	db, err := DB.DB()
	if err != nil {
		log.Fatalf("Database connection failure：%s\n", err.Error())
	}
	defer db.Close()
	lastBarIndex := strings.LastIndex(outDir, "/")
	packageName := outDir
	if lastBarIndex > -1 {
		packageName = outDir[lastBarIndex+1:]
	}
	if outDir == "" {
		outDir = path.Join(findProRoot(), "api/v1")
	}
	flist, err := ioutil.ReadDir(outDir)
	if err != nil {
		log.Fatalf("ReadDir %s failure: %s\n", outDir, err.Error())
	}
	protoList := make([]string, 0, 2)
	for _, v := range flist {
		if strings.HasSuffix(v.Name(), ".proto") {
			protoList = append(protoList, v.Name())
		}
	}
	if len(protoList) == 0 {
		log.Fatalf("No proto files")
	}
	var protoFile string
	survey.AskOne(&survey.Select{
		Message: "select output file",
		Options: protoList,
		Default: 0,
	}, &protoFile)
	if protoFile == "" {
		return
	}
	data := readTableStruct(DB, dbName, tableName, packageName)
	for _, v := range data {
		t, err := template.New("message").Funcs(template.FuncMap{
			"inc": func(i int) int {
				return i + 1
			},
		}).Parse(messageTmp)
		if err != nil {
			log.Fatalf("Template parsing failure：%s\n", err.Error())
		}
		buf := new(bytes.Buffer)
		for _, v := range v.ColumnList {
			v.Column = base.Camel2Case(v.Column)
			switch v.Type {
			case "int8", "int16":
				v.Type = "int32"
			case "int", "int32", "int64":
				v.Type = "int64"
			case "float64", "float32":
				v.Type = "double"
			case "time":
				v.Type = "google.protobuf.Timestamp"
			default:
				v.Type = "string"
			}
		}
		err = t.Execute(buf, v)
		if err != nil {
			log.Fatalf("Template parsing failure：%s\n", err.Error())
		}
		fi, err := os.OpenFile(path.Join(outDir, protoFile), os.O_WRONLY|os.O_APPEND, 0644)
		if err != nil {
			log.Fatalf("WriteFile failure：%s\n", err.Error())
		}
		fi.Write(buf.Bytes())
		fi.Close()
		log.Printf("success")
	}
}

var messageTmp = `
message {{.DoName}} {
{{- range $i, $v := .ColumnList}}
  {{$v.Type}} {{$v.Column}} = {{inc $i}};{{- if $v.Comment }} // {{$v.Comment}}{{- end }}
{{- end }}
}
`
